package uds.data;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

/* Classe importante permettant de remplir la base de données et de manipuler les données
 * présentes dans cette dernière */

public class POI_Database
{
	// Informations base de données
	private static final int VERSION_BDD = 1;
	private static final String NOM_BDD = "database_POI.db";
	
	// Noms des attributs de la base de données
	private static final String TABLE_POI = "table_poi";
	private static final String COL_ID = "id";
	private static final String COL_NAME = "name";
	private static final String COL_LATITUDE = "latitude";
	private static final String COL_LONGITUDE = "longitude";
	private static final String COL_FLOOR = "floor";
	private static final String COL_TYPE = "type";
	private static final String COL_DESCRIPTION = "description";
	private static final String COL_URL = "url";
	private static final String COL_URLS_IMAGES = "urls_image";
	private static final String COL_TAGS = "tags";
	private static final String COL_HISTORIC = "historic";
	private static final String COL_RELATED_TO_POI = "related_to_poi";
	
	// Numéro de colonne des attributs de la base de données
	//private static final int NUM_COL_CLES = 0;
	private static final int NUM_COL_ID = 1;
	private static final int NUM_COL_NAME = 2;
	private static final int NUM_COL_LATITUDE = 3;
	private static final int NUM_COL_LONGITUDE = 4;
	private static final int NUM_COL_FLOOR = 5;
	private static final int NUM_COL_TYPE = 6;
	private static final int NUM_COL_DESCRIPTION = 7;
	private static final int NUM_COL_URL = 8;
	private static final int NUM_COL_URLS_IMAGES = 9;
	private static final int NUM_COL_TAGS = 10;
	private static final int NUM_COL_HISTORIC = 11;
	private static final int NUM_COL_RELATED_TO_POI = 12;
	
	// Base de données
	private SQLiteDatabase bdd;
	private StructureBaseSQL baseSQLite;
	
	// Constructeur de la classe permettant de créer la base de données si elle n'existe pas encore (utile au premier lancement de l'application)
	public POI_Database (Context context)
	{
		baseSQLite = new StructureBaseSQL (context, NOM_BDD, null, VERSION_BDD);
	}
	
	// Méthode permettant d'accéder à la base de données en écriture
	public void open()
	{
		bdd = baseSQLite.getWritableDatabase();
	}
 
	// Méthode permettant de fermer la connexion à la base de données
	public void close()
	{
		bdd.close();
	}
 
	// Méthode permettant de retourner l'instance de la base de données instanciée
	public SQLiteDatabase getBDD()
	{
		return bdd;
	}
	
	// Méthode retournant la chaine de connexion de la base de données
	public static InputStream getInputStream(URL url) 
	{
		try 
		{
			return url.openConnection().getInputStream();
		} 
	    catch (IOException e) 
	    {
		    return null;
        }
	}
	
	// Méthode importante lancée lors du premier lancement de l'application et lorsque l'utilisateur souhaite mettre à jour les données de l'application
	public boolean initialisation_database()
	{
		// Initialisation de la base
        POI_Database base_poi = this;
        this.open();
        URL url;
        String tmp;
        XmlPullParserFactory factory;
        POI_Entry data = null;
        boolean inItem = false;
        // Importation des données du webservice distant et récupération des données dans un flux XML
		try
		{
			System.out.println("Importations des données terminées !");
			url = new URL("http://ks369878.kimsufi.com/cgi-bin/oduds-api.py?method=search&id=allpois");
			factory = XmlPullParserFactory.newInstance();
			factory.setNamespaceAware(true);
			XmlPullParser xpp = factory.newPullParser();
			xpp.setInput(getInputStream(url), "UTF_8");
			// Traitement des données XML et importation de ces dernières dans la base de données locale
			 int eventType = xpp.getEventType();
			 while (eventType != XmlPullParser.END_DOCUMENT) // Tant que le fichier XML n'est pas traité en entier
			 {
				 if (eventType == XmlPullParser.START_DOCUMENT) // Si on est au début du fichier XML
				 {
					base_poi.open();
				 } 
				 else if (eventType == XmlPullParser.START_TAG) // Dès qu'on se trouve au début d'une balise XMl ouvrante
				 {
						if (xpp.getName().equals("poi")) // Si c'est une balise "poi" on crée une nouvelle données
						{
							inItem = true;
							data = new POI_Entry();
						} 
						else if (xpp.getName().equals("id")) // On ajoute maintenant à cette nouvelle données, tous les attributs qu'elle contient
						{
							if (inItem)
							{
								tmp = xpp.nextText();
								data.setId(tmp);
							}
						}
						else if (xpp.getName().equals("name"))
						{
							if (inItem)
							{
								tmp = xpp.nextText();
								data.setName(tmp);
							}
						}
						else if (xpp.getName().equals("latitude"))
						{
							if (inItem)
							{
								tmp = xpp.nextText();
								data.setLatitude(tmp);
							}
						}
						else if (xpp.getName().equals("longitude"))
						{
							if (inItem)
							{
								tmp = xpp.nextText();
								data.setLongitude(tmp);
							}
						}
						else if (xpp.getName().equals("floor"))
						{
							if (inItem)
							{
								tmp = xpp.nextText();
								data.setFloor(tmp);
							}
						}
						else if (xpp.getName().equals("type"))
						{
							if (inItem)
							{
								tmp = xpp.nextText();
								data.setType(tmp);
							}
						}
						else if (xpp.getName().equals("description"))
						{
							if (inItem)
							{
								tmp = xpp.nextText();
								data.setDescription(tmp);
							}
						}
						else if (xpp.getName().equals("url"))
						{
							if (inItem)
							{
								tmp = xpp.nextText();
								data.setUrl(tmp);
							}
						}
						else if (xpp.getName().equals("urls_images"))
						{
							if (inItem)
							{
								tmp = xpp.nextText();
								data.setUrls_images(tmp);
							}
						}
						else if (xpp.getName().equals("tags"))
						{
							if (inItem)
							{
								tmp = xpp.nextText();
								data.setTags(tmp);
							}
						}
						else if (xpp.getName().equals("historic"))
						{
							if (inItem)
							{
								tmp = xpp.nextText();
								data.setHistoric(tmp);
							}
						}
						else if (xpp.getName().equals("related_to_poi"))
						{
							if (inItem)
							{
								tmp = xpp.nextText();
								data.setRelated_to_poi(tmp);
							}
						}
				 } 
				 else if (eventType == XmlPullParser.END_TAG) // Dès qu'on rencontre une balise XML fermeante "poi"
				 {
						if (xpp.getName().equals("poi"))
						{
							inItem = false;
							base_poi.insertPOI(data); // On insère la nouvelle données dans la base de données
						}
				 }
				 eventType = xpp.next(); // On passe à la données suivante
			 }
			 base_poi.close();
			 System.out.println("Importation des données effectuées !");
		}
		catch (Exception e) 
		{
			e.printStackTrace();
			System.out.println("Problème d'importation des données !");
			return false;
		}
		return true;
		// Code retour de la fonction permet de savoir si l'importation s'est bien passée ou non
	}
	
	// Fonction permettant de calculer le nombre de jour entre 2 dates
	public static double getDaysBetweenDates(Date theEarlierDate, Date theLaterDate) 
	{
		double result = Double.POSITIVE_INFINITY;
		if (theEarlierDate != null && theLaterDate != null) 
		{
			final long MILLISECONDS_PER_DAY = 1000 * 60 * 60 * 24;
			Calendar aCal = Calendar.getInstance();
			aCal.setTime(theEarlierDate);
			long aFromOffset = aCal.get(Calendar.DST_OFFSET);
			aCal.setTime(theLaterDate);
			long aToOffset = aCal.get(Calendar.DST_OFFSET);
			long aDayDiffInMili = (theLaterDate.getTime() + aToOffset) - (theEarlierDate.getTime() + aFromOffset);
			result = ((double) aDayDiffInMili / MILLISECONDS_PER_DAY);
		}
		return result;
	}
	
	// Méthode permettant de vider tout le contenu de la base de données locale (méthode utilisée pour l'actualisation des données)
	public boolean dropAll()
	{
		POI_Database base_poi = this;
        this.open();
		boolean res = true;
		System.out.println("ok");
		int nb = bdd.delete(TABLE_POI, "1", null);
		System.out.println("Nombre de ligne supprimée : " + nb);
		base_poi.close();
		return res;
	}

	// Méthode permettant d'insérer une nouvelle données (un nouveau point d'intérêt) dans la base de données
	public long insertPOI(POI_Entry data)
	{
		ContentValues values = new ContentValues();
		values.put(COL_ID, data.getId());
		values.put(COL_NAME, data.getName());
		values.put(COL_LONGITUDE, data.getLongitude());
		values.put(COL_LATITUDE, data.getLatitude());
		values.put(COL_FLOOR, data.getFloor());
		values.put(COL_TYPE, data.getType());
		values.put(COL_DESCRIPTION, data.getDescription());
		values.put(COL_URL, data.getUrl());
		values.put(COL_URLS_IMAGES, data.getUrls_images());
		values.put(COL_TAGS, data.getTags());
		values.put(COL_HISTORIC, data.getHistoric());
		values.put(COL_RELATED_TO_POI, data.getRelated_to_poi());
		return bdd.insert(TABLE_POI, null, values);
	}
	
	// Méthode permettant d'insérer une liste de données (une liste de point d'intérêt) dans la base de données
	public void insertListePOI(ArrayList<POI_Entry> list_poi)
	{
		for (POI_Entry entry : list_poi)
		{
			insertPOI(entry);
		}
	}
	
	// Méthode permettant de mettre à jour un point d'intérêt
	public int updatePOI(int id, POI_Entry data)
	{
		ContentValues values = new ContentValues();
		values.put(COL_ID, data.getId());
		values.put(COL_NAME, data.getName());
		values.put(COL_LONGITUDE, data.getLongitude());
		values.put(COL_LATITUDE, data.getLatitude());
		values.put(COL_FLOOR, data.getFloor());
		values.put(COL_TYPE, data.getType());
		values.put(COL_DESCRIPTION, data.getDescription());
		values.put(COL_URL, data.getUrl());
		values.put(COL_URLS_IMAGES, data.getUrls_images());
		values.put(COL_TAGS, data.getTags());
		values.put(COL_HISTORIC, data.getHistoric());
		values.put(COL_RELATED_TO_POI, data.getRelated_to_poi());
		return bdd.update(TABLE_POI, values, COL_ID + " = " + id, null);
	}
	
	// Méthode permattant de supprimer un point d'intérêt avec l'attribut "id" en paramètre
	public int removePOIWithID(int id)
	{
		return bdd.delete(TABLE_POI, COL_ID + " = " + id, null);
	}
 
	// Convertit un objet "Cursor" en un objet "POI_Entry"
	private POI_Entry cursorToPOI_Entry(Cursor c)
	{
		if (c.getCount() == 0) // On teste si le cursor est vide
		{
			return null;
		}
		c.moveToFirst(); // On se place sur le premier élément si non vide
		POI_Entry new_data = new POI_Entry();
		// Ajout des propriétés à new_data
		new_data.setId(c.getString(NUM_COL_ID));
		new_data.setName(c.getString(NUM_COL_NAME));
		new_data.setLatitude(c.getString(NUM_COL_LATITUDE));
		new_data.setLongitude(c.getString(NUM_COL_LONGITUDE));
		new_data.setFloor(c.getString(NUM_COL_FLOOR));
		new_data.setType(c.getString(NUM_COL_TYPE));
		new_data.setDescription(c.getString(NUM_COL_DESCRIPTION));
		new_data.setUrl(c.getString(NUM_COL_URL));
		new_data.setUrls_images(c.getString(NUM_COL_URLS_IMAGES));
		new_data.setTags(c.getString(NUM_COL_TAGS));
		new_data.setHistoric(c.getString(NUM_COL_HISTORIC));
		new_data.setRelated_to_poi(c.getString(NUM_COL_RELATED_TO_POI));
		c.close();
		return new_data;
	}
	
	// Convertit un objet "Cursor" (contenant le résultat d'une requête avec plusieurs tuples) en objet "ArrayList"
	private ArrayList<POI_Entry> cursorToArrayList(Cursor c)
	{
		ArrayList<POI_Entry> liste_poi = new ArrayList<POI_Entry>();
		if (c != null)
		{
			c.moveToFirst(); // Permet de se déplacer au premier élément du cursor
		}
		if (c.moveToFirst()) 
		{
		    do 
		    {
		        POI_Entry new_data = new POI_Entry();
		 		new_data.setId(c.getString(NUM_COL_ID));
				new_data.setName(c.getString(NUM_COL_NAME));
				new_data.setLatitude(c.getString(NUM_COL_LATITUDE));
				new_data.setLongitude(c.getString(NUM_COL_LONGITUDE));
				new_data.setFloor(c.getString(NUM_COL_FLOOR));
				new_data.setType(c.getString(NUM_COL_TYPE));
				new_data.setDescription(c.getString(NUM_COL_DESCRIPTION));
				new_data.setUrl(c.getString(NUM_COL_URL));
				new_data.setUrls_images(c.getString(NUM_COL_URLS_IMAGES));
				new_data.setTags(c.getString(NUM_COL_TAGS));
				new_data.setHistoric(c.getString(NUM_COL_HISTORIC));
				new_data.setRelated_to_poi(c.getString(NUM_COL_RELATED_TO_POI));
				liste_poi.add(new_data);
		    } while (c.moveToNext());
		}
		c.close();
		return liste_poi;
	}
	
	// Méthode permettant de sélectionner un point d'intérêt grace à son attribut "id"
	public POI_Entry getPOI_EntryWithId (String id)
	{
		Cursor c = bdd.query(TABLE_POI, null, COL_ID + " = " + id, null, null, null, null);
		return cursorToPOI_Entry(c);
	}
	
	// Méthode permettant de sélectionner les ressources associées à un point d'intérêt grace à son attribut "col_related_to_poi"
	public ArrayList<POI_Entry> getChildsPOI_Entry(String poi)
	{
		ArrayList<POI_Entry> liste_poi = new ArrayList<POI_Entry>();
		Cursor c = bdd.query(TABLE_POI, null, COL_RELATED_TO_POI + " = \'" + poi + "\'", null, null, null, COL_NAME);
		liste_poi = cursorToArrayList(c);
		return liste_poi;
	}
	
	// Méthode permettant de sélectionner des points d'intérêt par type de ressource
	public ArrayList<POI_Entry> getPOI_EntryWithType(String type)
	{
		ArrayList<POI_Entry> liste_poi = new ArrayList<POI_Entry>();
		String clause_where = COL_TYPE  + " = \'" + type + "\'";
		Cursor c = bdd.query(TABLE_POI, null, clause_where, null, null, null, COL_NAME); // On demande toutes les données de la table
		liste_poi = cursorToArrayList(c);
		return liste_poi;
	}
	
	// Méthode permettant de récupérer la liste compléte des types de ressource présente dans la base de données (par exemple batiment, salle)
	public ArrayList<String> getListeRessources()
	{
		ArrayList<String> liste_ressources = new ArrayList<String>();
		String[] tab_colonnes = {"DISTINCT " + COL_TYPE};
		Cursor c = bdd.query(TABLE_POI, tab_colonnes, null, null, null, null, COL_TYPE);
		if (c != null)
		{
			c.moveToFirst();
		}
		if (c.moveToFirst()) 
		{
		    do 
		    {
				liste_ressources.add(c.getString(0));
		    } while (c.moveToNext());
		}
		return liste_ressources;
	}
	
	// Méthode permettant de récupérer tout le contenu de la base de données
	public ArrayList<POI_Entry> getAllPOI_Entry()
	{
		ArrayList<POI_Entry> liste_poi = new ArrayList<POI_Entry>();
		Cursor c = bdd.query(TABLE_POI, null, null, null, null, null, COL_NAME); // On demande toutes les données de la table
		liste_poi = cursorToArrayList(c);
		return liste_poi;
	}
}
